package system

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"server/config"
	"server/plugin/common/util"
	"server/plugin/db"
	"time"

	"github.com/redis/go-redis/v9"
)

/*
	影视采集站点信息
*/

type SourceGrade int

const (
	MasterCollect SourceGrade = iota
	SlaveCollect
)

type CollectResultModel int

const (
	JsonResult CollectResultModel = iota
	XmlResult
)

type ResourceType int

func (rt ResourceType) GetActionType() string {
	var ac string = ""
	switch rt {
	case CollectVideo:
		ac = "detail"
	case CollectArticle:
		ac = "article"
	case CollectActor:
		ac = "actor"
	case CollectRole:
		ac = "role"
	case CollectWebSite:
		ac = "web"
	default:
		ac = "detail"
	}
	return ac
}

const (
	CollectVideo = iota
	CollectArticle
	CollectActor
	CollectRole
	CollectWebSite
)

// FilmSource 影视站点信息保存结构体
type FilmSource struct {
	Id           string             `json:"id"`           // 唯一ID
	Name         string             `json:"name"`         // 采集站点备注名
	Uri          string             `json:"uri"`          // 采集链接
	ResultModel  CollectResultModel `json:"resultModel"`  // 接口返回类型, json || xml
	Grade        SourceGrade        `json:"grade"`        // 采集站等级 主站点 || 附属站
	SyncPictures bool               `json:"syncPictures"` // 是否同步图片到服务器
	CollectType  ResourceType       `json:"collectType"`  // 采集资源类型
	State        bool               `json:"state"`        // 是否启用
	Interval     int                `json:"interval"`     // 采集时间间隔 单位/ms
}

// SaveCollectSourceList 保存采集站Api列表
func SaveCollectSourceList(list []FilmSource) error {
	var zl []redis.Z
	for _, v := range list {
		m, _ := json.Marshal(v)
		zl = append(zl, redis.Z{Score: float64(v.Grade), Member: m})
	}
	return db.Rdb.ZAdd(db.Cxt, config.FilmSourceListKey, zl...).Err()
}

// GetCollectSourceList 获取采集站API列表
func GetCollectSourceList() []FilmSource {
	// 先尝试从Redis缓存获取
	l, err := db.Rdb.ZRange(db.Cxt, config.FilmSourceListKey, 0, -1).Result()
	if err != nil {
		log.Println("Redis error:", err)
	}

	// 如果缓存中没有数据，从数据库获取并同步到缓存
	if len(l) == 0 {
		log.Println("Cache is empty, loading from database...")
		dbSources := GetFilmSourcesFromDB()
		if len(dbSources) > 0 {
			// 转换数据库数据为缓存格式
			filmSources := convertDBToFilmSource(dbSources)
			// 同步到缓存
			err := SaveCollectSourceList(filmSources)
			if err != nil {
				log.Println("Failed to sync data to cache:", err)
			}
			return filmSources
		}
		return nil
	}

	return getCollectSource(l)
}

// GetCollectSourceListByGrade 返回指定类型的采集Api信息 Master | Slave
func GetCollectSourceListByGrade(grade SourceGrade) []FilmSource {
	// 先尝试从Redis缓存获取
	s := fmt.Sprintf("%d", grade)
	zl, err := db.Rdb.ZRangeByScore(db.Cxt, config.FilmSourceListKey, &redis.ZRangeBy{Max: s, Min: s}).Result()
	if err != nil {
		log.Println("Redis error:", err)
	}

	// 如果缓存中没有数据，从数据库获取
	if len(zl) == 0 {
		log.Println("Cache is empty for grade", grade, ", loading from database...")
		var dbSources []FilmSourceDB
		db.Mdb.Where("grade = ?", grade).Find(&dbSources)
		if len(dbSources) > 0 {
			// 转换数据库数据为缓存格式
			filmSources := convertDBToFilmSource(dbSources)
			// 同步到缓存（这里只同步当前等级的数据）
			for _, fs := range filmSources {
				data, _ := json.Marshal(fs)
				db.Rdb.ZAdd(db.Cxt, config.FilmSourceListKey, redis.Z{Score: float64(fs.Grade), Member: data})
			}
			return filmSources
		}
		return nil
	}

	return getCollectSource(zl)
}

// FindCollectSourceById 通过Id标识获取对应的资源站信息
func FindCollectSourceById(id string) *FilmSource {
	for _, v := range GetCollectSourceList() {
		if v.Id == id {
			return &v
		}
	}
	return nil
}

// 将 []string 转化为 []FilmSourceApi
func getCollectSource(sl []string) []FilmSource {
	var l []FilmSource
	for _, s := range sl {
		f := FilmSource{}
		_ = json.Unmarshal([]byte(s), &f)
		l = append(l, f)
	}
	return l
}

// DelCollectResource 通过Id删除对应的采集站点信息
func DelCollectResource(id string) error {
	// 先从数据库删除
	result := db.Mdb.Where("source_id = ?", id).Delete(&FilmSourceDB{})
	if result.Error != nil {
		return fmt.Errorf("从数据库删除失败: %v", result.Error)
	}

	if result.RowsAffected == 0 {
		return errors.New("要删除的采集站不存在")
	}

	// 再从缓存删除
	for _, v := range GetCollectSourceList() {
		if v.Id == id {
			data, _ := json.Marshal(v)
			err := db.Rdb.ZRem(db.Cxt, config.FilmSourceListKey, data).Err()
			if err != nil {
				log.Println("从缓存删除失败:", err)
				// 缓存删除失败不影响整体操作，只记录日志
			}
			break
		}
	}

	return nil
}

// AddCollectSource 添加采集站信息
func AddCollectSource(s FilmSource) error {
	// 先检查数据库中是否存在重复的URI
	var count int64
	db.Mdb.Model(&FilmSourceDB{}).Where("uri = ?", s.Uri).Count(&count)
	if count > 0 {
		return errors.New("当前采集站点信息已存在, 请勿重复添加")
	}

	// 生成一个短uuid
	s.Id = util.GenerateSalt()

	// 先保存到数据库
	dbSource := convertFilmSourceToDB(s)
	err := db.Mdb.Create(&dbSource).Error
	if err != nil {
		return fmt.Errorf("添加到数据库失败: %v", err)
	}

	// 再添加到缓存
	data, _ := json.Marshal(s)
	err = db.Rdb.ZAddNX(db.Cxt, config.FilmSourceListKey, redis.Z{Score: float64(s.Grade), Member: data}).Err()
	if err != nil {
		log.Println("添加到缓存失败:", err)
		// 缓存添加失败不影响整体操作，只记录日志
	}

	return nil
}

// UpdateCollectSource 更新采集站信息
func UpdateCollectSource(s FilmSource) error {
	// 先检查数据库中是否存在重复的URI（排除当前ID）
	var count int64
	db.Mdb.Model(&FilmSourceDB{}).Where("uri = ? AND source_id != ?", s.Uri, s.Id).Count(&count)
	if count > 0 {
		return errors.New("当前采集站链接已存在其他站点中, 请勿重复添加")
	}

	// 检查要更新的记录是否存在
	var existingSource FilmSourceDB
	result := db.Mdb.Where("source_id = ?", s.Id).First(&existingSource)
	if result.Error != nil {
		return errors.New("要更新的采集站不存在")
	}

	// 先更新数据库 - 只更新指定字段，保持CreatedAt不变
	updateData := map[string]interface{}{
		"name":          s.Name,
		"uri":           s.Uri,
		"result_model":  s.ResultModel,
		"grade":         s.Grade,
		"sync_pictures": s.SyncPictures,
		"collect_type":  s.CollectType,
		"state":         s.State,
		"interval":      s.Interval,
		"updated_at":    time.Now(),
	}

	err := db.Mdb.Model(&FilmSourceDB{}).Where("source_id = ?", s.Id).Updates(updateData).Error
	if err != nil {
		return fmt.Errorf("更新数据库失败: %v", err)
	}

	// 再更新缓存
	// 删除缓存中的旧数据（只删除缓存，不删除数据库）
	delFromCacheOnly(s.Id)
	// 添加新数据到缓存
	data, _ := json.Marshal(s)
	err = db.Rdb.ZAdd(db.Cxt, config.FilmSourceListKey, redis.Z{Score: float64(s.Grade), Member: data}).Err()
	if err != nil {
		log.Println("更新缓存失败:", err)
		// 缓存更新失败不影响整体操作，只记录日志
	}

	return nil
}

// ClearAllCollectSource 删除所有采集站信息
func ClearAllCollectSource() {
	db.Rdb.Del(db.Cxt, config.FilmSourceListKey)
}

// ExistCollectSourceList 查询是否已经存在站点list相关数据
func ExistCollectSourceList() bool {
	// 先检查Redis缓存
	if db.Rdb.Exists(db.Cxt, config.FilmSourceListKey).Val() > 0 {
		return true
	}

	// 如果缓存中没有，检查数据库
	var count int64
	db.Mdb.Model(&FilmSourceDB{}).Count(&count)
	if count > 0 {
		// 如果数据库中有数据，同步到缓存
		err := SyncCacheFromDB()
		if err != nil {
			log.Println("Failed to sync from DB to cache:", err)
		}
		return true
	}

	return false
}

// FilmSourceDB MySQL数据库版本的影视站点信息结构体
type FilmSourceDB struct {
	ID           uint               `gorm:"primaryKey;autoIncrement" json:"id"`
	SourceId     string             `gorm:"uniqueIndex;size:50;not null" json:"source_id"` // 唯一ID
	Name         string             `gorm:"size:100;not null" json:"name"`                 // 采集站点备注名
	Uri          string             `gorm:"size:500;not null" json:"uri"`                  // 采集链接
	ResultModel  CollectResultModel `gorm:"type:tinyint;default:0" json:"result_model"`    // 接口返回类型, json || xml
	Grade        SourceGrade        `gorm:"type:tinyint;default:1" json:"grade"`           // 采集站等级 主站点 || 附属站
	SyncPictures bool               `gorm:"default:false" json:"sync_pictures"`            // 是否同步图片到服务器
	CollectType  ResourceType       `gorm:"type:tinyint;default:0" json:"collect_type"`    // 采集资源类型
	State        bool               `gorm:"default:false" json:"state"`                    // 是否启用
	Interval     int                `gorm:"default:0" json:"interval"`                     // 采集时间间隔 单位/ms
	CreatedAt    time.Time          `json:"created_at"`
	UpdatedAt    time.Time          `json:"updated_at"`
}

// TableName 指定表名
func (FilmSourceDB) TableName() string {
	return "film_sources"
}

// CreateFilmSourceTable 创建采集源表
func CreateFilmSourceTable() {
	var fs = &FilmSourceDB{}
	// 如果不存在则创建表
	if !ExistFilmSourceTable() {
		err := db.Mdb.AutoMigrate(fs)
		if err != nil {
			log.Println("Create Table FilmSource Failed: ", err)
		} else {
			log.Println("Create Table FilmSource Success")
		}
	}
}

// ExistFilmSourceTable 判断表中是否存在FilmSource表
func ExistFilmSourceTable() bool {
	return db.Mdb.Migrator().HasTable(&FilmSourceDB{})
}

// InitFilmSourceData 初始化采集源数据
func InitFilmSourceData() {
	// 检查表中是否已有数据
	var count int64
	db.Mdb.Model(&FilmSourceDB{}).Count(&count)
	if count > 0 {
		log.Println("FilmSource table already has data, skip initialization")
		return
	}

	// 初始化数据
	var sources = []FilmSourceDB{
		{SourceId: util.GenerateSalt(), Name: "HD(LZ)", Uri: "https://cj.lziapi.com/api.php/provide/vod/", ResultModel: JsonResult, Grade: SlaveCollect, SyncPictures: false, CollectType: CollectVideo, State: false, Interval: 0},
		{SourceId: util.GenerateSalt(), Name: "HD(BF)", Uri: "https://bfzyapi.com/api.php/provide/vod/", ResultModel: JsonResult, Grade: SlaveCollect, SyncPictures: false, CollectType: CollectVideo, State: false, Interval: 2500},
		{SourceId: util.GenerateSalt(), Name: "HD(FF)", Uri: "http://cj.ffzyapi.com/api.php/provide/vod/", ResultModel: JsonResult, Grade: SlaveCollect, SyncPictures: false, CollectType: CollectVideo, State: false, Interval: 0},
		{SourceId: util.GenerateSalt(), Name: "HD(OK)", Uri: "https://okzyapi.com/api.php/provide/vod/", ResultModel: JsonResult, Grade: SlaveCollect, SyncPictures: false, CollectType: CollectVideo, State: false, Interval: 0},
		{SourceId: util.GenerateSalt(), Name: "HD(HM)", Uri: "https://json.heimuer.xyz/api.php/provide/vod/", ResultModel: JsonResult, Grade: SlaveCollect, SyncPictures: false, CollectType: CollectVideo, State: false, Interval: 0},
		{SourceId: util.GenerateSalt(), Name: "HD(LY)", Uri: "https://360zy.com/api.php/provide/vod/at/json", ResultModel: JsonResult, Grade: SlaveCollect, SyncPictures: false, CollectType: CollectVideo, State: false, Interval: 0},
		{SourceId: util.GenerateSalt(), Name: "HD(SN)", Uri: "https://suoniapi.com/api.php/provide/vod/from/snm3u8/", ResultModel: JsonResult, Grade: SlaveCollect, SyncPictures: false, CollectType: CollectVideo, State: false, Interval: 2000},
		{SourceId: util.GenerateSalt(), Name: "HD(DB)", Uri: "https://caiji.dbzy.tv/api.php/provide/vod/from/dbm3u8/at/josn/", ResultModel: JsonResult, Grade: SlaveCollect, SyncPictures: false, CollectType: CollectVideo, State: false, Interval: 0},
		{SourceId: util.GenerateSalt(), Name: "HD(IK)", Uri: "https://ikunzyapi.com/api.php/provide/vod/at/json", ResultModel: JsonResult, Grade: SlaveCollect, SyncPictures: false, CollectType: CollectVideo, State: false, Interval: 0},
	}

	// 批量插入数据
	result := db.Mdb.Create(&sources)
	if result.Error != nil {
		log.Println("Init FilmSource data failed: ", result.Error)
	} else {
		log.Printf("Init FilmSource data success, inserted %d records", result.RowsAffected)
	}
}

// GetFilmSourcesFromDB 从数据库获取所有采集源
func GetFilmSourcesFromDB() []FilmSourceDB {
	var sources []FilmSourceDB
	db.Mdb.Find(&sources)
	return sources
}

// GetEnabledFilmSourcesFromDB 从数据库获取启用的采集源
func GetEnabledFilmSourcesFromDB() []FilmSourceDB {
	var sources []FilmSourceDB
	db.Mdb.Where("state = ?", true).Find(&sources)
	return sources
}

// convertDBToFilmSource 将数据库结构体转换为缓存结构体
func convertDBToFilmSource(dbSources []FilmSourceDB) []FilmSource {
	var filmSources []FilmSource
	for _, db := range dbSources {
		fs := FilmSource{
			Id:           db.SourceId,
			Name:         db.Name,
			Uri:          db.Uri,
			ResultModel:  db.ResultModel,
			Grade:        db.Grade,
			SyncPictures: db.SyncPictures,
			CollectType:  db.CollectType,
			State:        db.State,
			Interval:     db.Interval,
		}
		filmSources = append(filmSources, fs)
	}
	return filmSources
}

// convertFilmSourceToDB 将缓存结构体转换为数据库结构体
func convertFilmSourceToDB(filmSource FilmSource) FilmSourceDB {
	return FilmSourceDB{
		SourceId:     filmSource.Id,
		Name:         filmSource.Name,
		Uri:          filmSource.Uri,
		ResultModel:  filmSource.ResultModel,
		Grade:        filmSource.Grade,
		SyncPictures: filmSource.SyncPictures,
		CollectType:  filmSource.CollectType,
		State:        filmSource.State,
		Interval:     filmSource.Interval,
		UpdatedAt:    time.Now(),
	}
}

// SyncCacheFromDB 从数据库同步数据到缓存
func SyncCacheFromDB() error {
	dbSources := GetFilmSourcesFromDB()
	if len(dbSources) == 0 {
		return errors.New("数据库中没有采集源数据")
	}

	// 清空缓存
	ClearAllCollectSource()

	// 转换并保存到缓存
	filmSources := convertDBToFilmSource(dbSources)
	return SaveCollectSourceList(filmSources)
}

// SyncDBFromCache 从缓存同步数据到数据库
func SyncDBFromCache() error {
	filmSources := GetCollectSourceList()
	if len(filmSources) == 0 {
		return errors.New("缓存中没有采集源数据")
	}

	// 清空数据库表
	db.Mdb.Where("1 = 1").Delete(&FilmSourceDB{})

	// 转换并保存到数据库
	var dbSources []FilmSourceDB
	for _, fs := range filmSources {
		dbSource := convertFilmSourceToDB(fs)
		dbSources = append(dbSources, dbSource)
	}

	result := db.Mdb.Create(&dbSources)
	if result.Error != nil {
		return result.Error
	}

	log.Printf("Synced %d records from cache to database", result.RowsAffected)
	return nil
}

// delFromCacheOnly 只从缓存中删除数据，不删除数据库
func delFromCacheOnly(id string) {
	for _, v := range GetCollectSourceList() {
		if v.Id == id {
			data, _ := json.Marshal(v)
			err := db.Rdb.ZRem(db.Cxt, config.FilmSourceListKey, data).Err()
			if err != nil {
				log.Println("从缓存删除失败:", err)
			}
			break
		}
	}
}
